﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Licensed MIT: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using DNA.Utility;

namespace DNA.Mvc.Data.Xml
{
    public class WidgetTemplateXmlRepository : IWidgetTemplateRepository
    {
        protected DirectoryInfo directory = null;
        private Dictionary<int, ModelRef> local;
        private List<string> categoryTables;

        class ModelRef
        {
            public ModelRef() { }
            public ModelRef(string path, string category = "shared")
            {
                Category = category;
                Path = path;
                IsLoaded = false;
            }

            public string Path { get; set; }

            public bool IsLoaded { get; set; }

            public Exception LoadException { get; set; }

            public string Category { get; set; }

            public void Set(string path, WidgetTemplate model)
            {
                Path = path;
                IsLoaded = true;
                Model = model;
            }

            private WidgetTemplate model = null;

            public WidgetTemplate Model
            {
                get
                {
                    if ((model == null) && (!IsLoaded) && (!string.IsNullOrEmpty(Path)))
                    {
                        try
                        {
                            model = XmlSerializerUtility.DeserializeFormXmlFile<WidgetTemplate>(Path);
                            model.Modified = File.GetLastWriteTimeUtc(Path); 
                            model.ID = (new DirectoryInfo(System.IO.Path.GetDirectoryName(Path))).Parent.Name + "\\" + model.Name;
                        }
                        catch (Exception e)
                        {
                            LoadException = e;
                            return null;
                        }
                    }
                    return model;
                }
                private set { model = value; }
            }
        }

        public WidgetTemplateXmlRepository(string installationPath)
        {
            if (string.IsNullOrEmpty(installationPath)) throw new ArgumentNullException("installationPath");
            directory = new DirectoryInfo(installationPath);
            if (!Directory.Exists(installationPath)) throw new System.IO.DirectoryNotFoundException(installationPath);
            var catDirs = directory.GetDirectories();
            categoryTables = new List<string>();
            local = new Dictionary<int, ModelRef>();

            foreach (var catDir in catDirs)
            {
                var dirInfos = catDir.GetDirectories();
                categoryTables.Add(catDir.Name);

                foreach (var dirInfo in dirInfos)
                {
                    try
                    {
                        string fileName = dirInfo.FullName + (dirInfo.FullName.EndsWith("\\") ? "" : "\\") + "config.xml";
                        if (System.IO.File.Exists(fileName))
                        {
                            var code = fileName.ToLower().GetHashCode();
                            local.Add(code, new ModelRef(fileName, catDir.Name.ToLower()));
                        }
                    }
                    catch
                    {
                        continue;
                    }
                }
            }
        }

        public IQueryable<WidgetTemplate> All()
        {
            return local.Values.Select(m => m.Model).OrderBy(m => m.Name).AsQueryable();
        }

        public IQueryable<WidgetTemplate> All(out int total, int index = 0, int size = 50)
        {
            var tmpls = All();
            total = tmpls.Count();
            return index > 0 ? tmpls.Skip(index).Take(size) : tmpls.Take(size);
        }

        public IQueryable<WidgetTemplate> Filter(System.Linq.Expressions.Expression<Func<WidgetTemplate, bool>> predicate)
        {
            var pre = predicate.Compile();
            return local.Values.Select(m => m.Model).Where(pre).AsQueryable();
        }

        public IQueryable<WidgetTemplate> Filter<Key>(System.Linq.Expressions.Expression<Func<WidgetTemplate, Key>> sortingSelector, System.Linq.Expressions.Expression<Func<WidgetTemplate, bool>> filter, out int total, SortingOrders sortby = SortingOrders.Asc, int index = 0, int size = 50)
        {
            throw new NotImplementedException();
        }

        public bool Contains(System.Linq.Expressions.Expression<Func<WidgetTemplate, bool>> predicate)
        {
            return local.Values.Select(m => m.Model).Count(predicate.Compile()) > 0;
        }

        /// <summary>
        /// Find WidgetTemplate by ID or path HashCode
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public WidgetTemplate Find(params object[] keys)
        {
            if (keys[0].GetType() == typeof(string))
                return local.Values.Select(m => m.Model).FirstOrDefault(m => m.ID.Equals(keys[0].ToString(), StringComparison.OrdinalIgnoreCase));

            if (keys[0].GetType() == typeof(int))
                return local[(int)keys[0]].Model;

            return null;
        }

        public WidgetTemplate Find(System.Linq.Expressions.Expression<Func<WidgetTemplate, bool>> predicate)
        {
            var pre = predicate.Compile();
            return local.Values.Select(m => m.Model).FirstOrDefault(pre);
        }

        public WidgetTemplate Create(WidgetTemplate tmpl)
        {
            if (tmpl == null)
                throw new ArgumentNullException("widgetTemplate");

            if (string.IsNullOrEmpty(tmpl.ID))
                throw new Exception("The WidgetTemplate haven't ID");

            var dirName = ResolveFullPath(tmpl.ID);
            // directory.FullName + "\\" + tmpl.ID;

            if (!Directory.Exists(dirName))
                Directory.CreateDirectory(dirName);

            var fileName = ResolveConfigFile(tmpl.ID);
            // dirName + "\\config.xml";
            XmlSerializerUtility.SerializeToXmlFile(fileName, tmpl);

            if (Find(tmpl.ID) != null)
                throw new Exception("The widget template is exists");

            var catName = new DirectoryInfo(Path.GetDirectoryName(fileName)).Name;
            var _model = new ModelRef() { Category = catName };

            _model.Set(fileName, tmpl);
            local.Add(GetCode(tmpl.ID), _model);
            return tmpl;
        }

        public void Delete(WidgetTemplate t)
        {
            if (t == null)
                throw new ArgumentNullException("WidgetTemplate could not be null.");

            if (string.IsNullOrEmpty(t.ID))
                throw new ArgumentException("WidgetTemplate.ID could not be null.");

            Delete(t.ID);
        }

        public int Delete(System.Linq.Expressions.Expression<Func<WidgetTemplate, bool>> predicate)
        {
            throw new NotImplementedException();
        }

        public WidgetTemplate Update(WidgetTemplate t)
        {
            if (t == null)
                throw new ArgumentNullException("WidgetTemplate could not be null.");

            if (string.IsNullOrEmpty(t.ID))
                throw new ArgumentException("WidgetTemplate.ID could not be null.");

            if (!Contains(t.ID))
                throw new Exception("WidgetTemplate is not found in current context.");

            string fileName = ResolveConfigFile(t.ID);
            local[GetCode(t.ID)].Set(fileName, t);

            if (File.Exists(fileName))
                File.Delete(fileName);

            XmlSerializerUtility.SerializeToXmlFile(fileName, t);
            return t;
        }

        public int Count()
        {
            return local.Count;
        }


        //public int Count
        //{
        //    get { return local.Count; }
        //}

        public void Dispose()
        {
            local.Clear();
            GC.SuppressFinalize(this);
        }

        public string[] GetCategories()
        {
            return categoryTables.ToArray();
        }

        public IEnumerable<WidgetTemplate> WithIn(string category)
        {
            string lowerCat = category.ToLower();
            return local.Values.Where(v => v.Category == lowerCat).Select(m => m.Model).OrderBy(m => m.Name).ToList();
        }

        public IEnumerable<WidgetTemplate> WithIn(string category, out int total, int index = 0, int size = 50)
        {
            var cats = WithIn(category);
            total = cats.Count();
            if (index > 0)
                cats = cats.Skip(index * size).ToList();
            return cats.Take(size);
        }

        public void AddCategory(string category)
        {
            var cats = GetCategories();
            if (cats.Contains(category))
                throw new Exception("Category is exists.");

            Directory.CreateDirectory(directory.FullName + "\\" + category);
            categoryTables.Add(category);
        }

        public bool DeleteCategory(string category)
        {
            var cats = GetCategories();

            if (!cats.Contains(category))
                throw new Exception("Category is not exists.");

            categoryTables.Remove(category);
            Directory.Delete(directory.FullName + "\\" + category, false);
            return true;
        }

        public bool Delete(string id)
        {
            if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(id);
            if (!Contains(id)) throw new Exception("WidgetTemplate not exists.");
            if (File.Exists(ResolveConfigFile(id)))
            {
                Directory.Delete(ResolveFullPath(id), true);
                local.Remove(GetCode(id));
                return true;
            }
            return false;
        }

        public bool Contains(string id)
        {
            return Find(id) != null;
        }

        private int GetCode(string id)
        {
            return ResolveConfigFile(id).ToLower().GetHashCode();
        }

        private string ResolveConfigFile(string id)
        {
            return ResolveFullPath(id) + "\\config.xml";
        }

        private string ResolveFullPath(string id)
        {
            return directory.FullName + (directory.FullName.EndsWith("\\") ? "" : "\\") + id;
        }


        public int Count(System.Linq.Expressions.Expression<Func<WidgetTemplate, bool>> predicate)
        {
            return local.Select(m => m.Value.Model).AsQueryable().Count(predicate);
        }

        public int Submit()
        {
            return 0;
        }
    }


}
